package copy.code;

import com.sun.tools.javac.api.JavacTrees;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.processing.JavacProcessingEnvironment;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.util.*;
import copy.application.Process;
import copy.exception.MethodParamException;
import copy.express.pojo.EqualsNode;
import copy.express.pojo.Node;
import copy.express.pojo.NodeStack;
import copy.express.pojo.NodeType;
import copy.express.pojo.type.Type;
import copy.pojo.MethodMeta;
import copy.pojo.PropertyMeta;
import copy.tools.StringStrTools;

import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.Element;
import java.util.Queue;

/**
 * 类作用: 真正的生成字节码的类
 */
public class GenerateClassCodeStrategy extends AbsEasyCopyClassCode  {

    private static GenerateClassCodeStrategy generateClassCodeStrategy ;

    private static ProcessingEnvironment processingEnv;

    public static void init(ProcessingEnvironment p){
        processingEnv = p;
    }
    public static Process getSingleBean(){
        if (generateClassCodeStrategy == null){
            generateClassCodeStrategy = new GenerateClassCodeStrategy(processingEnv);
        }
        return generateClassCodeStrategy;
    }

    public void createClass(JCTree.JCClassDecl jcClassDecl){

        for (Element element : methodBodyMap.keySet()) {
            try{
                //权限修饰符
                JCTree.JCModifiers publicModifier = treeMaker.Modifiers(0);
                //类名
                Name name = names.fromString(element.getSimpleName().toString()+"Impl");
                //泛型参数列表

                ListBuffer<JCTree.JCTypeParameter> params = new ListBuffer<>();
                //接口
                ListBuffer<JCTree.JCExpression> interfaceList = new ListBuffer<>();
                //字段和方法

                ListBuffer<JCTree> jcTreeMethodAndFieldList = new ListBuffer<>();
                JCTree.JCExpression e = jcClassDecl.extending;
                try{
                    interfaceList.add( treeMaker.Ident(names.fromString(element.getSimpleName().toString())) ) ;
                }catch (Exception e1){
                    e1.printStackTrace();
                }

                //遍历方法
                for (JCTree.JCMethodDecl jcMethodDecl : methodBodyMap.get(element).keySet()) {
                    //方法体
                    JCTree.JCBlock block = treeMaker.Block(0, methodBodyMap.get(element).get(jcMethodDecl));
                    //创建方法
                    JCTree.JCModifiers modifiers = jcMethodDecl.getModifiers();
                    JCTree.JCMethodDecl newJCMethodDecl = null;
                    try{
                        newJCMethodDecl = treeMaker.MethodDef(jcMethodDecl.sym, block);
                        newJCMethodDecl.getModifiers().flags = Flags.PUBLIC;
                    }catch (Exception e2){
                        e2.printStackTrace();
                    }

                    //新建方法添加到集合中
                    jcTreeMethodAndFieldList.add(newJCMethodDecl);
                }
                JCTree.JCClassDecl classDecl = treeMaker.ClassDef(publicModifier, name, params.toList(), treeMaker.Ident(names.fromString("Object")), interfaceList.toList(), jcTreeMethodAndFieldList.toList());
                jcClassDecl.defs = jcClassDecl.defs.append(classDecl);
            }catch (Exception e){
                e.printStackTrace();
            }
        }

    }
    protected GenerateClassCodeStrategy(ProcessingEnvironment processingEnv) {
        super(processingEnv);
        //初始化上下文环境
        Context context = ((JavacProcessingEnvironment) processingEnv).getContext();

        javacTrees = JavacTrees.instance(processingEnv);
        treeMaker = TreeMaker.instance(context);
        names = Names.instance(context);
    }




    /**
     * 作用: 节点中内容 生成 字节码表达式
     * @param node
     * @param methodDecl
     * @return
     */
    public JCTree.JCExpressionStatement getExpressionStateByNode(NodeType node,JCTree.JCMethodDecl methodDecl){
        JCTree.JCExpressionStatement expressStatement = null;
        if (node.getType() instanceof Type.FieldType && node.getStringValue().contains(".") ){//字段类型
            //调用方法
            //expressStatement  = doExeMethod(simpleClassName, methodName, List.of(treeMaker.Ident(methodParamter.getName())));
            expressStatement = getField(node.getStringValue(),methodDecl);
        }else if (node.getType() instanceof Type.FieldType && !node.getStringValue().contains(".") ){
            expressStatement = treeMaker.Exec(  treeMaker.Ident(    getVariableByParams(methodDecl.params,node.getStringValue()) .getName()   )    );
        }else if (node.getType() instanceof Type.ExpressStatement){//表达式类型
            expressStatement = (JCTree.JCExpressionStatement)node.getValue();
        }else if (node.getType() instanceof Type.IntNumberType){//整数类型
            //默认采用: Integer.valueOf(),方式进行解析
            expressStatement = treeMaker.Exec(  treeMaker.Literal( Integer.valueOf( (String) node.getValue() ) ));
        }else if (node.getType() instanceof Type.DoubleNumberType){//小数类型
            expressStatement = treeMaker.Exec(  treeMaker.Literal( Double.valueOf( (String) node.getValue() ) ));
        }else if (node.getType() instanceof Type.StringType){//字符串类型
            expressStatement = treeMaker.Exec(  treeMaker.Literal( (String) node.getValue()  ));
        }else if (node.getType() instanceof  Type.Boolean){
            expressStatement = treeMaker.Exec(  treeMaker.Literal( Boolean.valueOf(node.getStringValue())  ));
        }else if (node.getType() instanceof Type.EqualsType){
            NodeType leftNode =  ( (EqualsNode)node ).getLeftNode();
            JCTree.JCExpressionStatement leftExpressionStatement = getExpressionStateByNode(leftNode,methodDecl);
            NodeType rightNode =  ( (EqualsNode)node ).getRightNode();
            JCTree.JCExpressionStatement rightExpressionStatement = getExpressionStateByNode(rightNode,methodDecl);
            //语句
            JCTree.JCMethodInvocation methodInvoke = treeMaker.Apply(
                    List.nil(),
                    treeMaker.Select(
                            leftExpressionStatement.expr, // . 左边的内容
                            names.fromString("equals") // . 右边的内容
                    ),
                    List.of(rightExpressionStatement.expr)
            );

            expressStatement =  treeMaker.Exec(methodInvoke);

        }
        return expressStatement;
    }

    /**
     * 生成调用类型转换器字节码
     * @param statments
     * @param methodDecl
     * @param propertyMeta
     */
    @Override
    public JCTree.JCExpressionStatement genTypetransformCLassCode(ListBuffer<JCTree.JCStatement> statments, JCTree.JCMethodDecl methodDecl, PropertyMeta propertyMeta) {
        String typeTransformClassName = StringStrTools.getSimleNameByClassName(propertyMeta.getTypeTransform());
        JCTree.JCFieldAccess singleBean = treeMaker.Select(
                treeMaker.Ident(names.fromString(typeTransformClassName))
                , names.fromString("singleBean")
        );
        //调用实例对象上方法

        return doExeMethod(singleBean, "transform", List.of(  getField(propertyMeta.getResorcePropertyName(), methodDecl).expr  ));


    }







    /**
     * 多个字段情况,需要运算
     * 生辰字节码: 调用Get和Set方法
     * @param
     * @param methodDecl
     * @param propertyMeta
     *
     */
    @Override
    public JCTree.JCExpressionStatement getExpressionStateByNodeStack( JCTree.JCMethodDecl methodDecl, PropertyMeta propertyMeta, NodeStack nodeStack) {
        for (int i = 0; i < nodeStack.size(); i++) {
            NodeType symbolNode  = (NodeType)nodeStack.get(i);
            NodeType nodeOne = null;
            NodeType nodeTwo = null;
            JCTree.JCExpressionStatement oneExpress = null;
            JCTree.JCExpressionStatement twoExpress = null;
            JCTree.JCExpression  resExpress = null;
            if ( symbolNode.getType() instanceof Type.SymbolEnum ){
                nodeTwo = (NodeType)nodeStack.remove(i-1);//注意: 先移除最近的一个
                nodeOne = (NodeType)nodeStack.remove(i-2);//再移除远的那个.否则BUG
                //当前位置i
                //弹出前两个元素
                oneExpress= getExpressionStateByNode(nodeOne,methodDecl);//弹出第一个元素
                if (oneExpress == null){
                    throw new MethodParamException(methodDecl.getName().toString(),nodeOne.getStringValue());
                }
                twoExpress =  getExpressionStateByNode(nodeTwo,methodDecl);//弹出第二个元素
                if (twoExpress == null){
                    throw new MethodParamException(methodDecl.getName().toString(),nodeTwo.getStringValue());
                }
                i=i-2;
            }else{
                continue;
            }
            if (((Type.SymbolEnum) symbolNode.getType()).isBigDecimal){
                resExpress = doExeMethodByClassName("BigDecimalType", "bigDecimal", List.of(oneExpress.expr, twoExpress.expr,treeMaker.Literal(((Type.SymbolEnum) symbolNode.getType()).value),treeMaker.Literal(propertyMeta.getScale()),treeMaker.Literal(propertyMeta.getRound())  )).expr;

            }else{
                //将运算结果保存到i位置上
                //获取节点的运算符 --> 运算结果类型Node换成ExpressStatement
                resExpress = treeMaker.Binary( ((Type.SymbolEnum) symbolNode.getType()).tag, oneExpress.expr, twoExpress.expr);
            }
            symbolNode.setType(Type.ExpressStatement.DEFAULT);//更换节点类型: 运算符类型 变 运算的表达式类型
            symbolNode.setValue(treeMaker.Exec( resExpress  ));//更换数据: 运算符 变 运算的表达式
            if (nodeStack.size()==1)break;

        }
        //运算到最后,Stack中只有一个节点. 类型是: 表达式节点
        NodeType resultNodeExpression = (NodeType) nodeStack.pop();
         return  (JCTree.JCExpressionStatement)resultNodeExpression.getValue();

    }

























































    //=====================================以下内容: 废弃方法,无需参考!!!!!!!!==============================================================================================
    /**
     * 这是一个废弃方法,从始至终不会调用!!!!!!
     *      作用: 生成一个方法
     *          方法名: 类名_字段名
     *          方法的参数: 需要解析的对象
     *          方法的内容: 递归调用对象上get
     * @param jcClassDecl
     * @param methodDecl
     * @param methodMeta
     */
    public void genGetFieldValueMethod(JCTree.JCClassDecl jcClassDecl, JCTree.JCMethodDecl methodDecl, MethodMeta methodMeta) {
        //寻找字段节点
        for (PropertyMeta propertyMeta : methodMeta.getPropertyMetas()) {
            for (Node node : propertyMeta.getNodeStack().getQueue()) {
                if (node.getType() == Type.FieldType.DEFAULT){
                    //拼接名称
                    String value = (String)node.getValue();//stu1.name
                    String[] fields = value.split("\\.");
                    String methodName = "";
                    String className = "";
                    for (JCTree.JCVariableDecl parameter : methodDecl.getParameters()) {
                        if (parameter.getName().toString().equals(fields[0])){
                            className = parameter.getType().toString();
                            methodName = className+"_"+value.substring( value.indexOf(".")+1);
                        }
                    }
                    //尝试生成方法
                    JCTree.JCVariableDecl param3 = treeMaker.VarDef(treeMaker.Modifiers(Flags.PARAMETER), names.fromString("obj"),treeMaker.Ident(names.fromString(className)), null);
                    com.sun.tools.javac.util.List<JCTree.JCVariableDecl> parameters3 = com.sun.tools.javac.util.List.of(param3);
                    //尝试生成方法体
                    JCTree.JCBlock block = null;
                    Queue<String> queue = genQueueByArray(fields);
                    ListBuffer<JCTree.JCStatement> statments = new ListBuffer<>();
                    JCTree.JCIdent ident = treeMaker.Ident(param3.getName());
                    JCTree.JCExpressionStatement resultExpress = doGetField(queue, ident, methodDecl);
                    statments.append(treeMaker.Return(
                            resultExpress.expr
                    ));
                    block = treeMaker.Block(0, statments.toList());
                    //添加到类中
                    JCTree.JCMethodDecl resultMethodDecl = treeMaker.MethodDef(
                            treeMaker.Modifiers(Flags.PUBLIC+Flags.STATIC), // 方法限定值
                            names.fromString(methodName), // 方法名
                            treeMaker.Ident(names.fromString("Object")), // 返回类型
                            com.sun.tools.javac.util.List.nil(),
                            parameters3, // 入参
                            com.sun.tools.javac.util.List.nil(),
                            block,
                            null
                    );
                    jcClassDecl.defs = jcClassDecl.defs.prepend(resultMethodDecl);
                    param3.pos = 900;//解决bug: -1

                }
            }
        }
    }

    /**
     * 这是一个废弃方法,从始至终不会调用!!!!!!
     * 作用: 生成一个成员属性Map
     *      Map中添加数据
     *          key: 下标
     *          value: 方法参数名
     * @param jcClassDecl
     * @param methodDecl
     * @param methodMeta
     */
    protected void genArgsMap(JCTree.JCClassDecl jcClassDecl,JCTree.JCMethodDecl methodDecl, MethodMeta methodMeta) {
        //new创建一个对象
        JCTree.JCNewClass jcNewClass = treeMaker.NewClass(
                null,
                List.nil(),
                treeMaker.Ident(names.fromString("HashMap")),
                List.nil(),
                null
        );
        JCTree.JCVariableDecl singleVarible = treeMaker.VarDef(treeMaker.Modifiers(Flags.PUBLIC | Flags.STATIC), names.fromString(methodDecl.getName().toString()+"Map"), treeMaker.Ident(names.fromString("HashMap")), jcNewClass);
        jcClassDecl.defs = jcClassDecl.defs.prepend(singleVarible);
        ListBuffer<JCTree.JCStatement> statments = new ListBuffer<>();

        //添加静态代码块
        for (int i = 0; i < methodDecl.getParameters().size(); i++) {
            int index = i;
            String variableName = methodDecl.getParameters().get(i).getName().toString();
            //生成初始化语句
            JCTree.JCExpressionStatement exec1 = treeMaker.Exec(
                    treeMaker.Apply(
                            com.sun.tools.javac.util.List.nil(),
                            treeMaker.Select(
                                    treeMaker.Ident(names.fromString(methodDecl.getName().toString()+"Map")), // . 左边的内容
                                    names.fromString( "put"  ) // . 右边的内容
                            ),
                            com.sun.tools.javac.util.List.of(treeMaker.Literal(index),treeMaker.Literal(variableName))
                    )
            );
            statments.append(exec1);
        }

        JCTree.JCBlock block = treeMaker.Block(0, statments.toList());
        block.flags = Flags.STATIC;
        jcClassDecl.defs = jcClassDecl.defs.prepend(block);

    }




    //=====================================废弃方法============================================================================================

}
